﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using SoSlam.Common.Communication;
using SoSlam.Common.Communication.GameMessages;
using System.Diagnostics;
using SoTools.Communicator;

namespace SoSlam.Server.Console
{
    public class PlayerLogic
    {
        private Vector2 _gravity;

        private Vector2 _position;
        public Vector2 Position { get { return _position; } }

        private Vector2 _speed;
        public Vector2 Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }

        private float _power;
        public float Power
        {
            get { return _power; }
            set { _power = value; }
        }

        private PlayerState _playerState;
        public PlayerState PlayerState
        {
            get { return _playerState; }
            set
            {
                if (_playerState != value)
                {
                    _playerState = value;
                    _communicator.Publish(new PlayerStateMessage { State = value });
                    Debug.WriteLine(string.Format("PlayerState : {0}", _playerState.ToString()));
                }
            }
        }

        private bool _hasSlammed;

        private Communicator _communicator;

        public PlayerLogic(Communicator communicator)
        {
            _communicator = communicator;
        }

        public void Initialize()
        {
            _gravity = new Vector2(0, -10);
            _position = new Vector2(0, 290);
            _speed = new Vector2();

            _power = 100;

            _hasSlammed = false;

            PlayerState = PlayerState.Waiting;
        }

        public void Update(GameTime gameTime)
        {
            // Update speed
            _speed = _gravity * (gameTime.ElapsedGameTime * Constants.TimeAdjustmentFactor) + Speed;
            _position = Speed * (gameTime.ElapsedGameTime * Constants.TimeAdjustmentFactor) + Position;

            _power += gameTime.ElapsedGameTime * 4;
            _power = MathHelper.Clamp(Power, 0, 100);

            // If near the ground
            if (Position.Y < 0.1f)
            {
                _position.Y = MathHelper.Clamp(Position.Y, 0, 1000);

                if (Speed.X < 0.001f)
                {
                    PlayerState = PlayerState.OnTheGround;

                    return;
                }

                // Calculate the angle with the ground to check for bouncing
                var dotProd = Vector2.Dot(Vector2.Normalize(Speed), Vector2.UnitX);
                var angle = MathHelper.ToDegrees((float)Math.Acos(dotProd));

                // Bouncing
                if (angle < 30)
                {
                    _speed.Y *= -0.5f;
                    _speed.X *= 0.8f;
                }
                else
                {
                    _speed.Y = 0;
                    _speed.X = 0;
                }
            }

            Debug.WriteLine(String.Format("Position ({0:N1};{1:N1}) Speed ({2:N1};{3:N1}) Power {4:N1}",
                    _position.X, _position.Y,
                    _speed.X, _speed.Y,
                    _power));
        }

        /// <summary>
        /// Falling at start
        /// </summary>
        public void Fall()
        {
            this.PlayerState = PlayerState.FallingAtStart;
        }

        /// <summary>
        /// Squirrel gets slammed
        /// </summary>
        public void Slam()
        {
            if (PlayerState == PlayerState.FallingAtStart
                && !_hasSlammed
                && _position.X == 0
                && 0 <= _position.Y && _position.Y <= 120)
            {
                _hasSlammed = true;

                var initialVector = new Vector2(50, _position.Y);
                var dotProduct = Vector2.Dot(Vector2.Normalize(initialVector), Vector2.UnitX);
                var angle = (float)Math.Acos(dotProduct);

                _speed += new Vector2(200 * (float)Math.Cos(angle), 200 * (float)Math.Sin(angle));

                PlayerState = PlayerState.Flying;
            }
        }

        /// <summary>
        /// Squirrel flying
        /// </summary>
        /// <param name="amplitude"></param>
        public void Flight(float amplitude)
        {
            if (PlayerState == PlayerState.Flying && _position.X > 50)
            {
                Debug.WriteLine("{0:HH:mm:ss.fff} Flop {1} !", DateTime.Now, amplitude);
                if (_power > (3 * amplitude))
                {
                    _speed += (new Vector2(0, 15) * amplitude);
                    _power -= (3 * amplitude);
                    _power = MathHelper.Clamp(_power, 0, 100);
                }
            }
        }
    }
}
